package com.tcs.test.executor.lock;

import com.tcs.test.executor.base.BeseExecutorRun;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class WriteAndReadLock extends BeseExecutorRun {

    private final Logger logger = LoggerFactory.getLogger(WriteAndReadLock.class);

    private int index = 0;

    @Test
    public void test1() {
        try {
            super.go();
        } catch (InterruptedException e) {
            logger.error("", e);
        }
    }

    class ArrayQueue<E> {

        public ArrayQueue() {
        }

        public ArrayQueue(int size) {
            this.array = new Object[size];
        }

//		private final ReentrantLock lock = new ReentrantLock();

        private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(false);

        ReentrantReadWriteLock.ReadLock readLock = rwl.readLock();

        ReentrantReadWriteLock.WriteLock writeLock = rwl.writeLock();

        private Object[] array = new Object[10];

        private int index = 0;

        public void put(E e) throws InterruptedException {
            writeLock.lockInterruptibly();
            try {
                array[index] = e;
                logger.info(" put index : {} , index : {}", array[index], index);
                index++;
            } catch (Exception ex) {
            } finally {
                writeLock.unlock();
            }
        }

        @SuppressWarnings("unchecked")
        public E take() throws InterruptedException {
            readLock.lockInterruptibly();
            E e = null;
            try {
                index--;
                logger.info(" take index : {} , index : {}", array[index], index);
                e = (E) array[index];
            } catch (Exception ex) {
            } finally {
                readLock.unlock();
            }
            return e;
        }
    }

    class Put implements Runnable {

        private ArrayQueue<Integer> arrayQueue = null;

        public Put() {
        }

        public Put(ArrayQueue<Integer> arrayQueue) {
            this.arrayQueue = arrayQueue;
        }

        @Override
        public void run() {

            while (true) {
                try {
                    arrayQueue.put(++index);
                } catch (InterruptedException e) {
                    logger.error("", e);
                }
            }
        }
    }

    class Take implements Runnable {

        private ArrayQueue<Integer> arrayQueue = null;

        public Take() {}

        public Take(ArrayQueue<Integer> arrayQueue) {
            this.arrayQueue = arrayQueue;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    this.arrayQueue.take();
                } catch (InterruptedException e) {
                    logger.error("", e);
                }
            }
        }
    }

    @Override
    protected void service() {
        ArrayQueue<Integer> arrayQueue = new ArrayQueue<Integer>(100);
        Put put = new Put(arrayQueue);
        Take take = new Take(arrayQueue);
        Thread put1 = new Thread(put, "put1");
        Thread put2 = new Thread(put, "put2");
        Thread put3 = new Thread(put, "put3");
        Thread take1 = new Thread(take, "take1");
        Thread take2 = new Thread(take, "take2");
        Thread take3 = new Thread(take, "take3");
        put1.start();
        put2.start();
        put3.start();
        take1.start();
        take2.start();
        take3.start();
        try {
            Thread.sleep(5 * 1000L);
        } catch (InterruptedException e) {
            logger.error("", e);
        }
//        put1.interrupt();
    }
}
